#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define BUFFER_SIZE      1024

// DHCP消息类型
#define DHCP_DISCOVER 1
#define DHCP_OFFER    2
#define DHCP_REQUEST  3
#define DHCP_ACK      5

// DHCP头部结构
struct dhcp_header {
    unsigned char op;         // 操作码，1为请求，2为应答
    unsigned char htype;      // 硬件类型，1为以太网
    unsigned char hlen;       // 硬件地址长度，6为以太网
    unsigned char hops;       // 跳数
    unsigned int xid;         // 事务ID
    unsigned short secs;      // 秒数
    unsigned short flags;     // 标志
    unsigned int ciaddr;      // 客户IP地址
    unsigned int yiaddr;      // 你的IP地址
    unsigned int siaddr;      // 服务器IP地址
    unsigned int giaddr;      // 网关IP地址
    unsigned char chaddr[16]; // 客户硬件地址
    unsigned char sname[64];  // 服务器主机名
    unsigned char file[128];  // 启动文件名
    unsigned char options[312]; // 选项
};

int main() {
    int sockfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len;
    char buffer[BUFFER_SIZE];
    struct dhcp_header *dhcp_packet;
    unsigned int fixed_ip = inet_addr("192.168.1.2"); // 固定分配的IP地址
    unsigned int server_ip = inet_addr("192.168.1.1"); // 服务器IP
    unsigned int gateway_ip = inet_addr("192.168.1.1"); // 网关IP
    unsigned int subnet_mask = inet_addr("255.255.255.0"); // 子网掩码

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket error");
        exit(1);
    }

    // 绑定服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(DHCP_SERVER_PORT);

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind error");
        exit(1);
    }

    client_len = sizeof(client_addr);
    while (1) {
        // 接收DHCP发现报文
        memset(buffer, 0, BUFFER_SIZE);
        int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &client_len);
        if (n < 0) {
            perror("recvfrom error");
            continue;
        }

        dhcp_packet = (struct dhcp_header *)buffer;
        if (dhcp_packet->op == DHCP_DISCOVER) {
            // 构造DHCP提供报文
            dhcp_packet->op = DHCP_OFFER;
            dhcp_packet->yiaddr = fixed_ip; // 分配固定IP
            dhcp_packet->siaddr = server_ip;
            dhcp_packet->giaddr = gateway_ip;

            // 设置选项：子网掩码、网关、DHCP服务器、租约时间等
            unsigned char *options = dhcp_packet->options;
            options[0] = 53; options[1] = 1; options[2] = DHCP_OFFER; // 消息类型
            options[3] = 1; options[4] = 4; memcpy(&options[5], &subnet_mask, 4); // 子网掩码
            options[9] = 3; options[10] = 4; memcpy(&options[11], &gateway_ip, 4); // 网关
            options[15] = 54; options[16] = 4; memcpy(&options[17], &server_ip, 4); // DHCP服务器
            options[21] = 51; options[22] = 4; unsigned int lease = htonl(86400); memcpy(&options[23], &lease, 4); // 租约时间（1天）
            options[27] = 255; // 选项结束

            // 发送DHCP提供报文
            if (sendto(sockfd, buffer, n, 0, (struct sockaddr *)&client_addr, client_len) < 0) {
                perror("sendto error");
            }

            // 接收DHCP请求报文
            memset(buffer, 0, BUFFER_SIZE);
            n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &client_len);
            if (n < 0) {
                perror("recvfrom error");
                continue;
            }

            dhcp_packet = (struct dhcp_header *)buffer;
            if (dhcp_packet->op == DHCP_REQUEST) {
                // 构造DHCP确认报文
                dhcp_packet->op = DHCP_ACK;
                dhcp_packet->yiaddr = fixed_ip;
                dhcp_packet->siaddr = server_ip;
                dhcp_packet->giaddr = gateway_ip;

                options = dhcp_packet->options;
                options[0] = 53; options[1] = 1; options[2] = DHCP_ACK;
                options[3] = 1; options[4] = 4; memcpy(&options[5], &subnet_mask, 4);
                options[9] = 3; options[10] = 4; memcpy(&options[11], &gateway_ip, 4);
                options[15] = 54; options[16] = 4; memcpy(&options[17], &server_ip, 4);
                options[21] = 51; options[22] = 4; memcpy(&options[23], &lease, 4);
                options[27] = 255;

                // 发送DHCP确认报文
                if (sendto(sockfd, buffer, n, 0, (struct sockaddr *)&client_addr, client_len) < 0) {
                    perror("sendto error");
                }
            }
        }
    }

    close(sockfd);
    return 0;
}